#include <atomic>
#include <thread>
#include <vector>
#include <iostream>
#include <mutex>

class CAppSeqNo
{
public:
    uint32_t FetchAdd()
    {
        return m_atmSeqNo.fetch_add(1, std::memory_order_relaxed);
    }

    uint32_t FetchAddWithLock()
    {
        std::lock_guard<std::mutex> objLG(m_mtxForSeqNo);
        uint32_t uiSeqNo = m_uiSeqNoWithLock;
        m_uiSeqNoWithLock += 1;

        return uiSeqNo;
    }

    uint32_t FetchAddUnsafe()
    {
        uint32_t uiSeqNo = m_uiSeqNo;
        m_uiSeqNo += 1;

        return uiSeqNo;
    }


    void RunTest()
    {
        std::vector<std::thread> vecThreads;

        for (int i = 0; i < 4; i++)
        {
            vecThreads.push_back(std::thread([&](){
                this->FetchAddUnsafe();
                this->FetchAdd();
                this->FetchAddWithLock();
            }));
        }

        for (auto& refThread : vecThreads)
        {
            refThread.join();
        }

        std::cout << "m_uiSeqNo         : " << m_uiSeqNo << std::endl;
        std::cout << "m_uiSeqNoWithLock : " << m_uiSeqNoWithLock << std::endl;
        std::cout << "m_atmSeqNo        : " << m_atmSeqNo.load() << std::endl;
    }

private:
    std::atomic<uint32_t> m_atmSeqNo{0};
    uint32_t              m_uiSeqNo{0};

    std::mutex            m_mtxForSeqNo;
    uint32_t              m_uiSeqNoWithLock{0};
};


int main(int argc, char** argv)
{
    CAppSeqNo objApp;

    objApp.RunTest();

    return 0;
}


